home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 11
/
Cream of the Crop 11-1.iso
/
comm
/
slip_mud.zip
/
UMSLIP.ASM
< prev
next >
Wrap
Assembly Source File
|
1995-07-10
|
88KB
|
3,548 lines
; Copyright, 1988-1992, Russell Nelson, Crynwr Software
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General MakePublic License as published by
; the Free Software Foundation, version 1.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General MakePublic License for more details.
;
; You should have received a copy of the GNU General MakePublic License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
;
;
;
;
; Copyright 1993, University of Minnesota
;
; Changes at U of M: (slip@booombox.micro.umn.edu)
;
; Changed many things to make it possible to set serial parameters
; on the fly.
; Fixed many timing problems.
; Added output handshaking.
; Cleaned up loop timing code.
; Made all opens delayed.
; Configuration is done by another program (PHONE.EXE)
;
; Still need to:
; Compute correct timing delays.
; Add input handshaking.
; Timeout hung handshaking properly.
; 38,400 baud loses a few characters.
; Doesnt detect missing com port.
;
majver equ 1 ;version number of the infrastructure.
MAX_ADDR_LEN equ 16 ;maximum number of bytes in our address.
MAX_HANDLE equ 10 ;maximum number of handles.
MAX_P_LEN equ 8 ;maximum type length
MAX_MULTICAST equ 8 ;maximum number of multicast addresses.
HT equ 09h
CR equ 0dh
LF equ 0ah
;
; Packet Driver Error numbers
NO_ERROR equ 0 ;no error at all.
BAD_HANDLE equ 1 ;invalid handle number
NO_CLASS equ 2 ;no interfaces of specified class found
NO_TYPE equ 3 ;no interfaces of specified type found
NO_NUMBER equ 4 ;no interfaces of specified number found
BAD_TYPE equ 5 ;bad packet type specified
NO_MULTICAST equ 6 ;this interface does not support
;multicast
CANT_TERMINATE equ 7 ;this packet driver cannot terminate
BAD_MODE equ 8 ;an invalid receiver mode was specified
NO_SPACE equ 9 ;operation failed because of
;insufficient space
TYPE_INUSE equ 10 ;the type had previously been accessed,
;and not released.
BAD_COMMAND equ 11 ;the command was out of range, or not
;implemented
CANT_SEND equ 12 ;the packet couldn't be sent (usually
;hardware error)
CANT_SET equ 13 ;hardware address couldn't be changed
;(more than 1 handle open)
BAD_ADDRESS equ 14 ;hardware address has bad length or
;format
CANT_RESET equ 15 ;Couldn't reset interface (more than
;1 handle open).
BAD_IOCB equ 16 ;an invalid iocb was specified
;a few useful Ethernet definitions.
RUNT equ 60 ;smallest legal size packet, no fcs
GIANT equ 1514 ;largest legal size packet, no fcs
EADDR_LEN equ 6 ;Ethernet address length.
ARCADDR_LEN equ 1
BLUEBOOK equ 1
IEEE8023 equ 11
MakePublic macro Sym
;;; Public &Sym ; no publics now...
endm
MakeExternal macro Sym
;;; Extrn &Sym ; no externs now...
endm
;The following two macros are used to manipulate port addresses.
;Use loadport to initialize dx. Use setport to set a specific port on
;the board. setport remembers what the current port number is, but beware!
;setport assumes that code is being executed in the same order as the
;code is presented in the source file. Whenever this assumption is violated,
;you need to enter another loadport. Some, but not all examples are:
;in a loop with multiple setports, or a backward jump over a setport, or
;a forward jump over a setport. If you have any doubt, consult the
;individual driver sources for examples of usage. If you suspect that
;you have too few loadports, define the symbol "no_confidence" to a
;one. This will force a loadport before every setport. If you wish to turn
;it off for some of your code, redefine it to a zero.
loadport macro
mov dx,io_addr
port_no = 0
endm
;change the port number from the current value to the new value.
setport macro new_port_no
ifdef no_confidence ;define if you suspect that you don't
if no_confidence
loadport ; have enough loadports, i.e. dx is
endif
endif ; set to the wrong port.
if new_port_no - port_no EQ 1
inc dx
else
if new_port_no - port_no EQ -1
dec dx
else
if new_port_no - port_no NE 0
add dx,new_port_no - port_no
endif
endif
endif
port_no = new_port_no
endm
Print macro
call DosPrint
endm
Delay Macro
call DelaySub
endm
SlowIn macro
Delay
in al,dx
endm
SlowOut macro
Delay
out dx,al
endm
segmoffs struc ; defines offs as 0, segm as 2
offs dw ?
segm dw ?
segmoffs ends
CY equ 0001h
EI equ 0200h
iocb struc ; as_send_pkt structure
buffer dd ? ; Pointer to the buffer
len dw ? ; Its length
flags db ? ; Some flags
ret_code db ? ; Completion code
upcall dd ? ; I/O completion upcall
next dd ? ; Private next pointer (queue)
resv db 4 dup (?) ; Unused private data
iocb ends
DONE equ 1 ; I/O complete flag
CALLME equ 2 ; Please upcall me flag
send_queueempty macro
; Check if send queue is empty.
; Enter with interrupts disabled.
; Exit with zr (zero) if empty, nz (not zero) if not.
; Destroys ax.
mov ax, word ptr send_head ; Queue empty?
or ax, word ptr send_head+2
endm
send_peekqueue macro
; Peek into the queue and get the next entry.
; Enter with interrupts disabled.
; Exit with es:di -> iocb.
les di, send_head ; Get head segment:offset
endm
; Bits in sys_features
MICROCHANNEL equ 02 ; a micro channel computer
TWO_8259 equ 40h ; 2nd 8259 exists
; Bits in flagbyte
CALLED_ETOPEN equ 1 ; have called etopen
D_OPTION equ 2 ; delayed initialization
N_OPTION equ 4 ; Novell protocol conversion
W_OPTION equ 8 ; Windows upcall checking.
; Copyright, 1988-1992, Russell Nelson, Crynwr Software
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General MakePublic License as published by
; the Free Software Foundation, version 1.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General MakePublic License for more details.
;
; You should have received a copy of the GNU General MakePublic License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
code segment word
assume cs:code, ds:code
MakePublic phd_environ
org 2ch
phd_environ dw ?
MakePublic phd_dioa
org 80h
phd_dioa label byte
org 100h
start:
jmp start_1
MakeExternal start_1: near
even ;put the stack on a word boundary.
;we use our dioa for a stack space. Very hard usage has shown that only
; 27 bytes were being used, so 128 should be sufficient.
our_stack label byte
MakeExternal int_no: byte
MakePublic packet_int_no, is_at, sys_features, flagbyte,quiet
packet_int_no db 60h,0,0,0 ; interrupt to communicate.
is_at db 0 ; =1 if we're on an AT.
sys_features db 0 ; 2h = MC 40h = 2nd 8259
flagbyte db 0
quiet db 0
even
functions label word
dw f_not_implemented ;0
dw f_driver_info ;1
dw f_access_type ;2
dw f_release_type ;3
dw f_send_pkt ;4
dw f_terminate ;5
dw f_get_address ;6
dw f_reset_interface ;7
dw f_stop ;8
dw f_not_implemented ;9
dw f_get_parameters ;10
dw f_not_implemented ;11
dw f_as_send_pkt ;12
dw f_drop_pkt ;13
dw f_ser_func ;14
dw f_not_implemented ;15
dw f_not_implemented ;16
dw f_not_implemented ;17
dw f_not_implemented ;18
dw f_not_implemented ;19
dw f_set_rcv_mode ;20
dw f_get_rcv_mode ;21
dw f_set_multicast_list ;22
dw f_get_multicast_list ;23
dw f_get_statistics ;24
dw f_set_address ;25
MakeExternal driver_class: byte
MakeExternal driver_type: byte
MakeExternal driver_name: byte
MakeExternal driver_function: byte
MakeExternal parameter_list: byte
MakeExternal send_pkt: near
MakeExternal as_send_pkt: near
MakeExternal drop_pkt: near
MakeExternal get_address: near
MakeExternal set_address: near
MakeExternal terminate: near
MakeExternal reset_interface: near
MakeExternal xmit: near
MakeExternal recv: near
MakeExternal recv_exiting: near
MakeExternal etopen: near
MakeExternal rcv_modes: word ;count of modes followed by mode handles.
MakeExternal set_multicast_list: near
linc macro n ; inc a 32 bit integer
local a
inc n ;increment the low word
jne a ;go if not overflow
inc n+2 ;increment the high word
a:
endm
per_handle struc
in_use db 0 ;non-zero if this handle is in use.
packet_type db MAX_P_LEN dup(0);associated packet type.
packet_type_len dw 0 ;associated packet type length.
receiver dd 0 ;receiver handler.
receiver_sig db 8 dup(?) ;signature at the receiver handler.
class db ? ;interface class
per_handle ends
handles per_handle MAX_HANDLE dup(<>)
end_handles label byte
MakePublic multicast_count, multicast_addrs, multicast_broad
multicast_count dw 0 ;count of stored multicast addresses.
multicast_broad db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh ; entry for broadcast
multicast_addrs db MAX_MULTICAST*EADDR_LEN dup(?)
have_my_address db 0 ;nonzero if our address has been set.
my_address db MAX_ADDR_LEN dup(?)
my_address_len dw ?
rcv_mode_num dw 3
free_handle dw 0 ; temp, a handle not in use
found_handle dw 0 ; temp, handle for our packet
receive_ptr dd 0 ; the pkt receive service routine
MakePublic send_head, send_tail
send_head dd 0 ; head of transmit queue
send_tail dd 0 ; tail of transmit queue
statistics_list label dword
packets_in dw ?,?
packets_out dw ?,?
bytes_in dw ?,?
bytes_out dw ?,?
errors_in dw ?,?
errors_out dw ?,?
packets_dropped dw ?,? ;dropped due to no type handler.
savess dw ? ;saved during the stack swap.
savesp dw ?
regs struc ; stack offsets of incoming regs
_ES dw ?
_DS dw ?
_BP dw ?
_DI dw ?
_SI dw ?
_DX dw ?
_CX dw ?
_BX dw ?
_AX dw ?
_IP dw ?
_CS dw ?
_F dw ? ; flags, Carry flag is bit 0
regs ends
CY equ 0001h
EI equ 0200h
bytes struc ; stack offsets of incoming regs
dw ? ; es, ds, bp, di, si are 16 bits
dw ?
dw ?
dw ?
dw ?
_DL db ?
_DH db ?
_CL db ?
_CH db ?
_BL db ?
_BH db ?
_AL db ?
_AH db ?
bytes ends
MakePublic our_isr, their_isr
their_isr dd 0 ; original owner of pkt driver int
our_isr:
jmp our_isr_0 ;the required signature.
db 'PKT DRVR',0
our_isr_0:
assume ds:nothing
push ax
push bx
push cx
push dx
push si
push di
push bp
push ds
push es
cld
mov bx,cs ;set up ds.
mov ds,bx
assume ds:code
mov bp,sp ;we use bp to access the original regs.
and _F[bp],not CY ;start by clearing the carry flag.
mov bl,ah ;jump to the correct function.
mov bh,0
cmp bx,25 ;only twenty five functions right now.
mov dh,BAD_COMMAND ;in case we find a bad number.
ja our_isr_error
add bx,bx ;*2
call functions[bx]
jnc our_isr_return
our_isr_error:
mov _DH[bp],dh
or _F[bp],CY ;return their carry flag.
our_isr_return:
pop es
pop ds
pop bp
pop di
pop si
pop dx
pop cx
pop bx
pop ax
iret
MakePublic re_enable_interrupts
re_enable_interrupts:
; Possibly re-enable interrupts. We put this here so that other routines
; don't need to know how we put things on the stack.
test _F[bp], EI ; Were interrupts enabled on pkt driver entry?
je re_enable_interrupts_1 ; No.
sti ; Yes, re-enable interrupts now.
re_enable_interrupts_1:
ret
f_not_implemented:
mov dh,BAD_COMMAND
stc
ret
f_driver_info:
; As of 1.08, the handle is optional, so we no longer verify it.
; call verify_handle
cmp _AL[bp],0ffh ; correct calling convention?
jne f_driver_info_1 ; ne = incorrect, fail
;For enhanced PD, if they call
cmp _BX[bp],offset handles ;with a handle, give them the
;class they think it is
jb default_handle
cmp _BX[bp],offset end_handles ;otherwise default to first class
jae default_handle
mov bx, _BX[bp]
cmp [bx].in_use,0 ;if it's not in use, it's bad.
je default_handle
mov al, [bx].class
mov _CH[bp], al
jmp short got_handle
default_handle:
mov al,driver_class
mov _CH[bp],al
got_handle:
mov _BX[bp],majver ;version
mov al,driver_type
cbw
mov _DX[bp],ax
mov _CL[bp],0 ;number zero.
mov _DS[bp],ds ; point to our name in their ds:si
mov _SI[bp],offset driver_name
mov al,driver_function
mov _AL[bp],al
clc
ret
f_driver_info_1:
stc
ret
f_set_rcv_mode:
call verify_handle
cmp cx,rcv_mode_num ;are we already using that mode?
je f_set_rcv_mode_4 ;yes, no need to check anything.
mov dx,bx ;remember our handle.
mov bx,offset handles ; check that all handles are free
f_set_rcv_mode_2:
cmp bx,dx ; is this our handle?
je f_set_rcv_mode_3 ; yes, of course it's not free.
cmp [bx].in_use,0 ; is this handle free?
jne f_set_rcv_mode_1 ; ne = no, can't change
f_set_rcv_mode_3:
add bx,(size per_handle) ; next handle
cmp bx,offset end_handles ; examined all handles?
jb f_set_rcv_mode_2 ; b = no, continue examination
mov cx,_CX[bp] ;get the desired receive mode.
cmp cx,rcv_modes ;do they have this many modes?
jae f_set_rcv_mode_1 ;no - must be a bad mode for us.
mov bx,cx
add bx,bx ;we're accessing words, not bytes.
mov ax,rcv_modes[bx]+2 ;get the handler for this mode.
or ax,ax ;do they have one?
je f_set_rcv_mode_1 ;no - must be a bad mode for us.
mov rcv_mode_num,cx ;yes - remember the number and
call ax ; call it.
f_set_rcv_mode_4:
clc
ret
f_set_rcv_mode_1:
mov dh,BAD_MODE
stc
ret
f_get_rcv_mode:
call verify_handle
mov ax,rcv_mode_num ;return the current receive mode.
mov _AX[bp],ax
clc
ret
f_set_multicast_list:
mov cx,_CX[bp] ;Tell them how much room they have.
;verify that they supplied an even number of EADDR's.
mov ax,cx
xor dx,dx
mov bx,EADDR_LEN
div bx
or dx,dx ;zero remainder?
jne f_set_multicast_list_2 ;no, we don't have an even number of
; addresses.
cmp ax,MAX_MULTICAST ;is this too many?
ja f_set_multicast_list_3 ;yes - return NO_SPACE
f_set_multicast_list_1:
mov multicast_count,ax ;remember the number of addresses.
push cs
pop es
mov di,offset multicast_addrs
push ds
mov ds,_ES[bp] ; get ds:si -> new list.
mov si,_DI[bp]
push cx
rep movsb
pop cx
pop ds
mov si,offset multicast_addrs
call set_multicast_list
ret
f_set_multicast_list_2:
mov dh,BAD_ADDRESS
stc
ret
f_set_multicast_list_3:
mov dh,NO_SPACE
stc
ret
f_get_multicast_list:
mov _ES[bp],ds ;return what we have remembered.
mov _DI[bp],offset multicast_addrs
mov ax,EADDR_LEN ;multiply the count by the length.
mul multicast_count
mov _CX[bp],ax ;because they want total bytes.
clc
ret
f_get_statistics:
call verify_handle ;just in case.
mov _DS[bp],ds
mov _SI[bp],offset statistics_list
clc
ret
access_type_class:
mov dh,NO_CLASS
stc
ret
access_type_type:
mov dh,NO_TYPE
stc
ret
access_type_number:
mov dh,NO_NUMBER
stc
ret
access_type_bad:
mov dh,BAD_TYPE
stc
ret
;register caller of pkt TYPE
f_access_type:
mov bx, offset driver_class
access_type_9:
mov al, [bx] ;get the next class.
inc bx
or al,al ;end of the list?
je access_type_class ;class failed (story of my life)
cmp _AL[bp],al ;our class?
jne access_type_9 ;no, try again
access_type_1:
cmp _BX[bp],-1 ;generic type?
je access_type_2 ;yes.
mov al,driver_type
cbw
cmp _BX[bp],ax ;our type?
jne access_type_type ;no.
access_type_2:
cmp _DL[bp],0 ;generic number?
je access_type_3
cmp _DL[bp],1 ;our number?
jne access_type_number
access_type_3:
cmp _CX[bp],MAX_P_LEN ;is the type length too long?
ja access_type_bad ;yes - can't be ours.
; now we do two things--look for an open handle, and check the existing
; handles to see if they're replicating a packet type.
mov free_handle,0 ;remember no free handle yet.
mov bx,offset handles
access_type_4:
cmp [bx].in_use,0 ;is this handle in use?
je access_type_5 ;no - don't check the type.
mov al, _AL[bp] ;is this handle the same class as
cmp al, [bx].class ; they're want?
jne short access_type_6
mov es,_DS[bp] ;get a pointer to their type
mov di,_SI[bp] ; from their ds:si to our es:di
mov cx,_CX[bp] ;get the minimum of their length
; and our length. As currently
; implemented, only one receiver
; gets the packets, so we have to
; ensure that the shortest prefix
; is unique.
cmp cx,[bx].packet_type_len ;Are we less specific than they are?
jb access_type_8 ;no.
mov cx,[bx].packet_type_len ;yes - use their count.
access_type_8:
lea si,[bx].packet_type
or cx,cx ; pass-all TYPE? (zero TYPE length)
jne access_type_7 ; ne = no
mov bx,offset handles+(MAX_HANDLE-1)*(size per_handle)
jmp short access_type_5 ; put pass-all last
access_type_7:
repe cmpsb
jne short access_type_6 ;go look at the next one.
access_type_inuse:
mov dh,TYPE_INUSE ;a handle has been assigned for TYPE
stc ;and we can't assign another
ret
access_type_5: ;handle is not in use
cmp free_handle,0 ;found a free handle yet?
jne access_type_6 ;yes.
mov free_handle,bx ;remember a free handle
access_type_6:
add bx,(size per_handle) ;go to the next handle.
cmp bx,offset end_handles ;examined all handles?
jb access_type_4 ;no, continue.
mov bx,free_handle ;did we find a free handle?
or bx,bx
je access_type_space ;no - return error.
mov [bx].in_use,1 ;remember that we're using it.
mov ax,_DI[bp] ;remember the receiver type.
mov [bx].receiver.offs,ax
mov ax,_ES[bp]
mov [bx].receiver.segm,ax
push ds
mov ax,ds
mov es,ax
mov ds,_DS[bp] ;remember their type.
mov si,_SI[bp]
mov cx,_CX[bp]
mov es:[bx].packet_type_len,cx ; remember the TYPE length
lea di,[bx].packet_type
rep movsb
lds si,es:[bx].receiver ;copy the first 8 bytes
lea di,[bx].receiver_sig ; to the receiver signature.
mov cx,8/2
rep movsw
pop ds
mov al, _AL[bp]
mov [bx].class, al
mov _AX[bp],bx ;return the handle to them.
clc
ret
access_type_space:
mov dh,NO_SPACE
stc
ret
f_release_type:
call verify_handle ;mark this handle as being unused.
mov [bx].in_use,0
clc
ret
f_send_pkt:
;ds:si -> buffer, cx = length
; XXX Should re-enable interrupts here, but some drivers are broken.
; Possibly re-enable interrupts.
; test _F[bp], EI ; Were interrupts enabled on pkt driver entry?
; je f_send_pkt_1 ; No.
; sti ; Yes, re-enable interrupts now.
;f_send_pkt_1:
;following two instructions not needed because si and cx haven't been changed.
; mov si,_SI[bp]
; mov cx,_CX[bp] ; count of bytes in the packet.
linc packets_out
add bytes_out.offs,cx ;add up the received bytes.
adc bytes_out.segm,0
push ds ; set up proper ds for the buffer
mov ds,_DS[bp] ; address of buffer from caller's ds.
assume ds:nothing, es:nothing
; If -n option take Ethernet encapsulated Novell IPX packets (from BYU's
; PDSHELL) and change them to be IEEE 802.3 encapsulated.
EPROT_OFF equ EADDR_LEN*2
test cs:flagbyte,N_OPTION
jz f_send_pkt_2
cmp ds:[si].EPROT_OFF,3781h ; if not Novell (prot 8137)
jne f_send_pkt_2 ; don't tread on it
push ax ; get scratch reg
mov ax,[si].EPROT_OFF+4 ; get len
xchg ah,al
inc ax ; make even (rounding up)
and al,0feh
xchg ah,al
mov ds:[si].EPROT_OFF,ax ; save in prot field
pop ax ; restore old contents
f_send_pkt_2:
call send_pkt
pop ds
assume ds:code
ret
f_as_send_pkt:
;es:di -> iocb.
test driver_function,4 ; is this a high-performance driver?
je f_as_send_pkt_2 ; no.
; Possibly re-enable interrupts.
test _F[bp], EI ; Were interrupts enabled on pkt driver entry?
je f_as_send_pkt_1 ; No.
sti ; Yes, re-enable interrupts now.
f_as_send_pkt_1:
push ds ; set up proper ds for the buffer
lds si,es:[di].buffer ; ds:si -> buffer
assume ds:nothing
mov cx,es:[di].len ; cx = length
linc packets_out
add bytes_out.offs,cx ; add up the received bytes.
adc bytes_out.segm,0
;ds:si -> buffer, cx = length, es:di -> iocb.
call as_send_pkt
pop ds
assume ds:code
ret
f_as_send_pkt_2:
mov dh, BAD_COMMAND ; return an error.
stc
ret
f_drop_pkt:
; es:di -> iocb.
test driver_function,4 ; is this a high-performance driver?
je f_as_send_pkt_1 ; no.
push ds ; Preserve ds
mov si,offset send_head ; Get head offset
dp_loop:
mov ax,ds:[si] ; Get offset
mov dx,ds:[si+2] ; Get segment
mov bx,ax
or bx,dx ; End of list?
je dp_endlist ; Yes
cmp ax,di ; Offsets equal?
jne dp_getnext ; No
mov bx,es
cmp dx,bx ; Segments equal?
jne dp_getnext ; No
call drop_pkt ; Pass to driver
les di,es:[di].next ; Get next segment:offset
mov ds:[si],di ; Set next offset
mov ds:[si+2],es ; Set next segment
pop ds ; Restore ds
clc
ret
dp_getnext:
mov ds,dx ; Get next segment
mov si,ax ; Get next iocb offset
lea si,ds:[si].next ; Get next iocb next ptr offset
jmp dp_loop ; Try again
dp_endlist:
pop ds ; Restore ds
mov dh,BAD_IOCB ; Return error
stc ; Set carry
ret
MakeExternal ser_funcs:near
f_ser_func: ; direct serial functions
call ser_funcs
clc
ret
f_terminate:
call verify_handle ; must have a handle
f_terminate_1:
mov [bx].in_use,0 ; mark handle as free
mov bx,offset handles ; check that all handles are free
f_terminate_2:
cmp [bx].in_use,0 ; is this handle free?
jne f_terminate_4 ; ne = no, so can't exit completely
add bx,(size per_handle) ; next handle
cmp bx,offset end_handles ; examined all handles?
jb f_terminate_2 ; b = no, continue examination
;
; Now disable interrupts
;
call int_dis
call terminate ;terminate the hardware.
mov al,packet_int_no ;release our_isr.
mov ah,25h
push ds
lds dx,their_isr
int 21h
pop ds
;
; Now free our memory
;
push cs
pop es
mov ah,49h
int 21h
clc
ret
f_terminate_4:
mov dh, CANT_TERMINATE
stc
ret
MakePublic int_dis
int_dis:
mov al,int_no
or al,al ;are they using a hardware interrupt?
je f_term_no_irq ;no.
call maskint
;
; Now return the interrupt to their handler.
;
mov ah,25h ;get the old interrupt into es:bx
mov al,int_no
add al,8
cmp al,8+8 ;is it a slave 8259 interrupt?
jb f_term_3 ;no.
add al,70h - (8+8) ;map it to the real interrupt.
f_term_3:
push ds
lds dx,their_recv_isr
int 21h
pop ds
f_term_no_irq:
ret
f_get_address:
; call verify_handle
; mov es,_ES[bp] ; get new one
; mov di,_DI[bp] ; get pointer, es:di is ready
mov cx,_CX[bp] ;Tell them how much room they have.
cmp have_my_address,0 ;has our address been set?
jne get_address_set ;yes - go report it.
call get_address ;no, can we get the address?
jc get_address_space ;no - we must not have enough space.
mov _CX[bp],cx ;Tell them how long our address is.
clc
ret
get_address_set:
cmp cx,my_address_len ;is there enough room?
jb get_address_space ;no.
mov cx,my_address_len ;yes - get our address length.
mov _CX[bp],cx ;Tell them how long our address is.
mov si,offset my_address ;copy it into their area.
rep movsb
clc
ret
get_address_space:
mov dh,NO_SPACE
stc
ret
f_set_address:
mov bx,offset handles
mov cl,0 ;number of handles in use.
f_set_address_1:
add cl,[bx].in_use ;is this handle in use?
add bx,(size per_handle) ;go to the next handle.
cmp bx,offset end_handles
jb f_set_address_1
cmp cl,1 ;more than one handle in use?
ja f_set_address_inuse ;yes - we can't set the address
mov ds,_ES[bp] ; set new one
assume ds:nothing
mov si,_DI[bp] ; set pointer, ds:si is ready
mov cx,_CX[bp] ;Tell them how much address is being set.
call set_address
;set_address restores ds.
jc f_set_address_exit ;Did it work?
mov _CX[bp],cx ;yes - return our address length.
cmp cx,MAX_ADDR_LEN ;is it too long for us to remember?
ja f_set_address_too_long ;yes, return a too-long error.
mov ds,_ES[bp] ; set new one
mov si,_DI[bp] ; set pointer, ds:si is ready
mov ax,cs
mov es,ax
mov my_address_len,cx ;remember how long our address is.
mov di,offset my_address
rep movsb
mov have_my_address,1
mov ds,ax ;restoer ds.
assume ds:code
clc
ret
f_set_address_inuse:
mov dh,CANT_SET
stc
ret
f_set_address_too_long:
mov dh,NO_SPACE
stc
f_set_address_exit:
ret
f_reset_interface:
call verify_handle
call reset_interface
clc
ret
; Stop the packet driver doing upcalls. Also a following terminate will
; always succed (no in use handles any longer).
f_stop:
mov bx,offset handles
f_stop_2:
mov [bx].in_use,0
add bx,(size per_handle) ; next handle
cmp bx,offset end_handles
jb f_stop_2
clc
ret
f_get_parameters:
;strictly speaking, this function only works for high-performance drivers.
test driver_function,4 ;is this a high-performance driver?
jne f_get_parameters_1 ;yes.
mov dh,BAD_COMMAND ;no - return an error.
stc
ret
f_get_parameters_1:
mov _ES[bp],cs
mov _DI[bp],offset parameter_list
clc
ret
verify_handle:
;Ensure that their handle is real. If it isn't, we pop off our return
;address, and return to *their* return address with cy set.
mov bx,_BX[bp] ;get the handle they gave us
cmp bx,offset handles
jb verify_handle_bad ;no - must be bad.
cmp bx,offset end_handles
jae verify_handle_bad ;no - must be bad.
cmp [bx].in_use,0 ;if it's not in use, it's bad.
je verify_handle_bad
ret
verify_handle_bad:
mov dh,BAD_HANDLE
add sp,2 ;pop off our return address.
stc
ret
MakePublic set_recv_isr
set_recv_isr:
mov ah,35h ;get the old interrupt into es:bx
mov al,int_no ;board's interrupt vector
or al,al
je set_isr_no_irq
add al,8
cmp al,8+8 ;is it a slave 8259 interrupt?
jb set_recv_isr_1 ;no.
add al,70h - 8 - 8 ;map it to the real interrupt.
set_recv_isr_1:
int 21h
mov their_recv_isr.offs,bx ;remember the old seg:off.
mov their_recv_isr.segm,es
mov ah,25h ;now set our recv interrupt.
mov dx,offset recv_isr
int 21h
mov al,int_no ; Now enable interrupts
call unmaskint
set_isr_no_irq:
ret
MakePublic count_in_err
count_in_err:
assume ds:nothing
linc errors_in
ret
MakePublic count_out_err
count_out_err:
assume ds:nothing
linc errors_out
ret
MakePublic DelaySub
DelaySub:
push cx
pop cx
ret
their_recv_isr dd 0 ; original owner of board int
;
; I have had a problem with some hardware which under extreme LAN loading
; conditions will re-enter the recv_isr. Since the 8259 interrupts for
; the card are masked off, and the card's interrupt mask register is
; cleared (in 8390.asm at least) disabling the card from interrupting, this
; is clearly a hardware problem. Due to the low frequencey of occurance, and
; extreme conditions under which this happens, it is not lilely to be fixed
; in hardware any time soon, plus retrofitting of hardware in the field will
; not happen. To protect the driver from the adverse effects of this I am
; adding a simple re-entrancy trap here. - gft - 910617
;
in_recv_isr db 0 ; flag to trap re-entrancy
recv_isr:
cmp in_recv_isr, 0
je no_re_enter
iret
no_re_enter:
mov in_recv_isr, 1
; I realize this re-entrancy trap is not perfect, you could be re-entered
; anytime before the above instruction. However since the stacks have not
; been swapped re-entrancy here will only appear to be a spurious interrupt.
; - gft - 910617
; In order to achieve back-to-back packet transmissions, we handle the
; latency-critical portion of transmit interrupts first. The xmit
; interrupt routine should only start the next transmission, but do
; no other work. It may only touch ax and dx (the only register necessary
; for doing "out" instructions) unless it first pushes any other registers
; itself.
push ax
push dx
call xmit
; Now switch stacks, push remaining registers, and do remaining interrupt work.
push ds
mov ax,cs ;ds = cs.
mov ds,ax
assume ds:code
mov savesp,sp
mov savess,ss
mov ss,ax
mov sp,offset our_stack
cld
push bx
push cx
push si
push di
push bp
push es
; The following comment is wrong in that we now do a specific EOI command,
; and because we don't enable interrupts (even though we should).
; Chips & Technologies 8259 clone chip seems to be very broken. If you
; send it a Non Specific EOI command, it clears all In Service Register
; bits instead of just the one with the highest priority (as the Intel
; chip does and clones should do). This bug causes our interrupt
; routine to be reentered if: 1. we reenable processor interrupts;
; 2. we reenable device interrupts; 3. a timer or other higher priority
; device interrupt now comes in; 4. the new interrupting device uses
; a Non Specific EOI; 5. our device interrupts again. Because of
; this bug, we now completely mask our interrupts around the call
; to "recv", the real device interrupt handler. This allows us
; to send an EOI instruction to the 8259 early, before we actually
; reenable device interrupts. Since the interrupt is masked, we
; are still guaranteed not to get another interrupt from our device
; until the interrupt handler returns. This has another benefit:
; we now no longer prevent other devices from interrupting while our
; interrupt handler is running. This is especially useful if we have
; other (multiple) packet drivers trying to do low-latency transmits.
mov al,int_no ; Disable further device interrupts
call maskint
; The following is from Bill Rust, <wjr@ftp.com>
; this code dismisses the interrupt at the 8259. if the interrupt number
; is > 8 then it requires fondling two PICs instead of just one.
mov al, int_no ; get hardware int #
cmp al, 8 ; see if its on secondary PIC
jg recv_isr_4
add al, 60h ; make specific EOI dismissal
out 20h, al
jmp recv_isr_3 ; all done
recv_isr_4:
add al,60h - 8 ; make specific EOI (# between 9 & 15).
out 0a0h,al ; Secondary 8259 (PC/AT only)
mov al,62h ; Acknowledge on primary 8259.
out 20h,al
recv_isr_3:
; sti ; Interrupts are now completely safe
call recv
cli ;interrupts *must* be off between
;here and the stack restore, because
;if we have one of our interrupts
;pending, we would trash our stack.
;
; According to Novell IMSP Technical Memo #2, Lost Interrupts and NetWare,
; some LAN controllers can glitch the interrupt line when the interrupt
; mask register on the LAN controller is enabled to allow the LAN controller
; to interrupt. This can cause spurious/lost interrupt problems, especially
; on some 486 processors which latch the int line to the processor. To prevent
; glitches from being propogated throug, move the call to recv_exiting (re-
; enables interrupts on the LAN card) to before when the 8259 interrupts are
; unmasked. - gft - 910617
call recv_exiting
mov al,int_no ; Now reenable device interrupts
call unmaskint
pop es
pop bp
pop di
pop si
pop cx
pop bx
mov ss,savess
mov sp,savesp
; move the next call up to above the call unmaskint - gft - 910617
; call recv_exiting ;this routine can enable interrupts.
; DDP - This is a BIG mistake. This routine SHOULD NOT enable interrupts.
; doing so can cause interrupt recursion and blow your stack.
; Processor interrupts SHOULD NOT be enabled after enabling device
; interrupts until after the "iret". You will lose atleast 12 bytes
; on the stack for each recursion.
pop ds
assume ds:nothing
pop dx
pop ax
mov in_recv_isr, 0 ; clear the re-entrancy flag - gft - 901617
iret
MakePublic maskint
maskint:
or al,al ;are they using a hardware interrupt?
je maskint_1 ;no, don't mask off the timer!
assume ds:code
mov dx,21h ;assume the master 8259.
cmp al,8 ;using the slave 8259 on an AT?
jb mask_not_irq2
mov dx,0a1h ;go disable it on slave 8259
sub al,8
mask_not_irq2:
mov cl,al
SlowIn ;disable them on the correct 8259.
mov ah,1 ;set the bit.
shl ah,cl
or al,ah
;
; 500ns Stall required here, per INTEL documentation for eisa machines
; - gft - 910617
;
Delay
SlowOut
maskint_1:
ret
MakePublic unmaskint
unmaskint:
assume ds:code
mov dx,21h ;assume the master 8259.
mov cl,al
cmp cl,8 ;using the slave 8259 on an AT?
jb unmask_not_irq2 ;no
SlowIn ;get master mask
and al,not (1 shl 2) ; and clear slave cascade bit in mask
SlowOut ;set new master mask (enable slave int)
;
; 500ns Stall required here, per INTEL documentation for eisa machines
; - gft - 910617
;
mov dx,0a1h ;go enable int on slave 8259
sub cl,8
unmask_not_irq2:
SlowIn ;enable interrupts on the correct 8259.
mov ah,1 ;clear the bit.
shl ah,cl
not ah
and al,ah
;
; 500ns Stall required here, per INTEL documentation for eisa machines
; - gft - 910617
;
SlowOut
ret
MakePublic recv_find
recv_find:
;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type, dl = packet class.
;exit with es:di = 0 if the packet is not desired, or es:di -> packet buffer
; to be filled by the driver.
assume ds:code, es:nothing
push cx
; If -n option take IEEE 802.3 encapsulated packets that could be Novell IPX
; and make them Ethernet encapsulated Novell IPX packets (for PDSHELL).
test flagbyte,N_OPTION
jz not_n_op
; Make IEEE 802.3-like packets that could be Novell IPX into BlueBook class
; Novell type 8137 packets.
cmp dl,IEEE8023 ;Is this an IEEE 802.3 packet?
jne recv_not_802_3 ;no
cmp word ptr es:[di],0ffffh ;if this word not ffff
jne recv_not_8137 ; then not Novell
sub di,2 ; back it up to the 8137 word.
mov es:[di],3781h ; fake as Novell protocol (8137)
mov dl,BLUEBOOK
jmp short recv_not_8137
recv_not_802_3:
; Convert incoming Ethernet type 8137 IPX packets to type 8138, as with -n in
; effect we can't send type 8137, and it will only confuse Netware.
cmp dl,BLUEBOOK ;Is this a BLUEBOOK packet?
jne recv_not_8137 ;no, don't change it.
cmp word ptr es:[di],3781h ;Is it an 8137 packet?
jne recv_not_8137 ;no, don't change it.
mov es:[di],word ptr 3881h ;yes, mung it slightly.
recv_not_8137:
not_n_op:
mov bx,offset handles
recv_find_1:
cmp [bx].in_use,0 ;is this handle in use?
je recv_find_2 ;no - don't check the type.
mov ax,[bx].receiver.offs ;do they have a receiver?
or ax,[bx].receiver.segm
je recv_find_2 ;no - they're not serious about it.
mov cx,[bx].packet_type_len ;compare the packets.
lea si,[bx].packet_type
jcxz recv_find_3 ;if cx is zero, they want them all.
cmp [bx].class, dl ;is this the right class?
jne recv_find_2 ;no- don't bother
push di
repe cmpsb
pop di
je recv_find_3 ;we've got it!
recv_find_2:
add bx,(size per_handle) ;go to the next handle.
cmp bx,offset end_handles
jb recv_find_1
linc packets_dropped
pop cx ;we didn't find it -- discard it.
recv_find_5:
xor di,di ;"return" a null pointer.
mov es,di
ret
recv_find_3:
pop cx ; the packet_length
linc packets_in
add bytes_in.offs,cx ;add up the received bytes.
adc bytes_in.segm,0
les di,[bx].receiver ;remember the receiver upcall.
mov receive_ptr.offs,di
mov receive_ptr.segm,es
test flagbyte,W_OPTION ;did they select the Windows option?
je recv_find_6 ;no, don't check for the upcall.
; does the receiver signature match whats currently in memory? if not,
; jump to fake return
push si
push cx
lea si,[bx].receiver_sig
mov cx,8/2
repe cmpsw
pop cx
pop si
jne recv_find_5
recv_find_6:
mov found_handle,bx ;remember what our handle was.
mov ax,0 ;allocate request.
stc ;with stc, flags must be an odd number
push ax ; save a number that cant be flags
pushf ;save flags in case iret used.
call receive_ptr ;ask the client for a buffer.
; on return, flags should be at top of stack. if an IRET has been used,
; then 0 will be at the top of the stack
pop bx
cmp bx,0
je recv_find_4 ;0 is at top of stack
add sp,2
recv_find_4:
ret
MakePublic recv_copy
recv_copy:
;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
;preserve bx.
assume ds:nothing, es:nothing
push bx
mov bx,found_handle
mov ax,1 ;store request.
clc ;with clc, flags must be an even number
push ax ; save a number that can't be flags
pushf ;save flags incase iret used.
call receive_ptr ;ask the client for a buffer.
pop bx
cmp bx,1 ;if this is a 1, IRET was used.
je recv_copy_1
pop bx
recv_copy_1:
pop bx
ret
MakePublic send_queue
send_queue:
; Queue an iocb.
; Enter with es:di -> iocb, interrupts disabled.
; Destroys ds:si.
assume ds:nothing, es:nothing
mov es:[di].next.offs,0 ; Zero next offset
mov es:[di].next.segm,0 ; Zero next segment
mov si,send_head.offs ; Queue empty?
or si,send_head.segm
jnz sq_notempty ; No
mov send_head.offs,di ; Set head offset
mov send_head.segm,es ; Set head segment
jmp sq_settail
sq_notempty: ; Queue is not empty
lds si,send_tail ; Get tail segment:offset
mov ds:[si].next.offs,di ; Set next offset
mov ds:[si].next.segm,es ; Set next segment
sq_settail:
mov send_tail.offs,di ; Set tail offset
mov send_tail.segm,es ; Set tail segment
ret
MakePublic send_dequeue
send_dequeue:
; Dequeue an iocb and possibly call its upcall.
; Enter with device or processor interrupts disabled, ah = return code.
; Exits with es:di -> iocb; destroys ds:si, ax, bx, cx, dx, bp.
assume ds:nothing, es:nothing
les di,send_head ; Get head segment:offset
lds si,es:[di].next ; Get next segment:offset
mov send_head.offs, si ; Set head offset
mov send_head.segm, ds ; Set head segment
or es:flags[di], DONE ; Mark done
mov es:ret_code[di], ah ; Set retcode
test es:[di].flags,CALLME ; Does he want an upcall?
je send_dequeue_1 ; No.
push es ; Push iocb segment
push di ; and offset
clc ; Clear carry.
mov ax,1 ; Push a number that cant be flags.
push ax
pushf ; Save flags in case iret used.
call es:[di].upcall ; Call the client.
pop ax ; Pop first word.
cmp ax,1 ; If this is a 1, IRET was used.
je send_dequeue_2 ; Far return used.
add sp,2 ; Pop flags.
send_dequeue_2:
pop di ; Pop iocb segment
pop es ; and offset
send_dequeue_1:
ret
code ends
;;;grg equ 99
base = 50
Alloc macro var,len
&var equ base*2
base = base+1+0&len
endm
Alloc col_chout
Alloc col_chin
Alloc col_ovrr,2
Alloc col_frame,2
Alloc col_serfull,2
Alloc col_TCPFull,2
Alloc col_InIn
Alloc col_InOut
Alloc col_inmove
Alloc col_paks
Show macro Col,Ch
ifdef grg
push es
push ax
mov ax,0B800h
mov es,ax
mov byte ptr es:[&col],&Ch ; twiddle screen if buf full
pop ax
pop es
endif
endm
ShowInc macro Col,Ch
ifdef grg
push es
push ax
mov ax,0B800h
mov es,ax
mov byte ptr es:[&col],&Ch
inc byte ptr es:[&col+2]
pop ax
pop es
endif
endm
TheVersion equ 1
majver equ 1 ;version number of the infrastructure.
MAX_ADDR_LEN equ 16 ;maximum number of bytes in our address.
MAX_HANDLE equ 10 ;maximum number of handles.
MAX_P_LEN equ 8 ;maximum type length
MAX_MULTICAST equ 8 ;maximum number of multicast addresses.
; Copyright, 1988-1992, Russell Nelson, Crynwr Software
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General MakePublic License as published by
; the Free Software Foundation, version 1.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General MakePublic License for more details.
;
; You should have received a copy of the GNU General MakePublic License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
HT equ 09h
CR equ 0dh
LF equ 0ah
;
; Packet Driver Error numbers
NO_ERROR equ 0 ;no error at all.
BAD_HANDLE equ 1 ;invalid handle number
NO_CLASS equ 2 ;no interfaces of specified class found
NO_TYPE equ 3 ;no interfaces of specified type found
NO_NUMBER equ 4 ;no interfaces of specified number found
BAD_TYPE equ 5 ;bad packet type specified
NO_MULTICAST equ 6 ;this interface does not support
;multicast
CANT_TERMINATE equ 7 ;this packet driver cannot terminate
BAD_MODE equ 8 ;an invalid receiver mode was specified
NO_SPACE equ 9 ;operation failed because of
;insufficient space
TYPE_INUSE equ 10 ;the type had previously been accessed,
;and not released.
BAD_COMMAND equ 11 ;the command was out of range, or not
;implemented
CANT_SEND equ 12 ;the packet couldn't be sent (usually
;hardware error)
CANT_SET equ 13 ;hardware address couldn't be changed
;(more than 1 handle open)
BAD_ADDRESS equ 14 ;hardware address has bad length or
;format
CANT_RESET equ 15 ;Couldn't reset interface (more than
;1 handle open).
BAD_IOCB equ 16 ;an invalid iocb was specified
;a few useful Ethernet definitions.
RUNT equ 60 ;smallest legal size packet, no fcs
GIANT equ 1514 ;largest legal size packet, no fcs
EADDR_LEN equ 6 ;Ethernet address length.
ARCADDR_LEN equ 1
BLUEBOOK equ 1
IEEE8023 equ 11
;The following two macros are used to manipulate port addresses.
;Use loadport to initialize dx. Use setport to set a specific port on
;the board. setport remembers what the current port number is, but beware!
;setport assumes that code is being executed in the same order as the
;code is presented in the source file. Whenever this assumption is violated,
;you need to enter another loadport. Some, but not all examples are:
;in a loop with multiple setports, or a backward jump over a setport, or
;a forward jump over a setport. If you have any doubt, consult the
;individual driver sources for examples of usage. If you suspect that
;you have too few loadports, define the symbol "no_confidence" to a
;one. This will force a loadport before every setport. If you wish to turn
;it off for some of your code, redefine it to a zero.
loadport macro
mov dx,io_addr
port_no = 0
endm
;change the port number from the current value to the new value.
setport macro new_port_no
ifdef no_confidence ;define if you suspect that you don't
if no_confidence
loadport ; have enough loadports, i.e. dx is
endif
endif ; set to the wrong port.
if new_port_no - port_no EQ 1
inc dx
else
if new_port_no - port_no EQ -1
dec dx
else
if new_port_no - port_no NE 0
add dx,new_port_no - port_no
endif
endif
endif
port_no = new_port_no
endm
Print macro
call DosPrint
endm
Delay Macro
call DelaySub
endm
SlowIn macro
Delay
in al,dx
endm
SlowOut macro
Delay
out dx,al
endm
segmoffs struc ; defines offs as 0, segm as 2
offs dw ?
segm dw ?
segmoffs ends
CY equ 0001h
EI equ 0200h
iocb struc ; as_send_pkt structure
buffer dd ? ; Pointer to the buffer
len dw ? ; Its length
flags db ? ; Some flags
ret_code db ? ; Completion code
upcall dd ? ; I/O completion upcall
next dd ? ; Private next pointer (queue)
resv db 4 dup (?) ; Unused private data
iocb ends
DONE equ 1 ; I/O complete flag
CALLME equ 2 ; Please upcall me flag
send_queueempty macro
; Check if send queue is empty.
; Enter with interrupts disabled.
; Exit with zr (zero) if empty, nz (not zero) if not.
; Destroys ax.
mov ax, word ptr send_head ; Queue empty?
or ax, word ptr send_head+2
endm
send_peekqueue macro
; Peek into the queue and get the next entry.
; Enter with interrupts disabled.
; Exit with es:di -> iocb.
les di, send_head ; Get head segment:offset
endm
; Bits in sys_features
MICROCHANNEL equ 02 ; a micro channel computer
TWO_8259 equ 40h ; 2nd 8259 exists
; Bits in flagbyte
CALLED_ETOPEN equ 1 ; have called etopen
D_OPTION equ 2 ; delayed initialization
N_OPTION equ 4 ; Novell protocol conversion
W_OPTION equ 8 ; Windows upcall checking.
;Ported from Phil Karn's asy.c and slip.c, a C-language driver for the IBM-PC
;8250 by Russell Nelson. Any bugs are due to Russell Nelson.
;16550 support ruthlessly stolen from Phil Karn's 8250.c.
; Bugs by Denis DeLaRoca
; Stopped failures from lost transmit interrupts (by eliminating the ints
; altogether). Remove unneeded transmitter buffer.
; Version 6 by Joe Doupnik, jrd@cc.usu.edu, Utah State University, Dec 1991.
; Copyright, 1988, 1991, Russell Nelson
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General MakePublic License as published by
; the Free Software Foundation, version 1.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General MakePublic License for more details.
;
; You should have received a copy of the GNU General MakePublic License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
code segment word
assume cs:code, ds:code
;8250 definitions
;Control/status register offsets from base address
THR equ 0 ;Transmitter holding register
RBR equ 0 ;Receiver buffer register
DLL equ 0 ;Divisor latch LSB
DLM equ 1 ;Divisor latch MSB
IER equ 1 ;Interrupt enable register
IIR equ 2 ;Interrupt ident register
FCR equ 2 ;16550 FIFO control register
LCR equ 3 ;Line control register
MCR equ 4 ;Modem control register
LSR equ 5 ;Line status register
MSR equ 6 ;Modem status register
;8250 Line Control Register
LCR_5BITS equ 0 ;5 bit words
LCR_6BITS equ 1 ;6 bit words
LCR_7BITS equ 2 ;7 bit words
LCR_8BITS equ 3 ;8 bit words
LCR_NSB equ 4 ;Number of stop bits
LCR_PEN equ 8 ;Parity enable
LCR_EPS equ 10h ;Even parity select
LCR_SP equ 20h ;Stick parity
LCR_SB equ 40h ;Set break
LCR_DLAB equ 80h ;Divisor Latch Access Bit
;16550 FIFO control register values
FIFO_ENABLE equ 001h ;Enable TX and RX fifo
FIFO_CLR_RX equ 002h ;Clear RX fifo
FIFO_CLR_TX equ 004h ;Clear TX fifo
FIFO_START_DMA equ 008h ;Enable TXRDY/RXRDY pin DMA handshake
FIFO_SIZE_1 equ 000h ;RX fifo trigger levels
FIFO_SIZE_4 equ 040h
FIFO_SIZE_8 equ 080h
FIFO_SIZE_14 equ 0c0h
FIFO_SIZE_MASK equ 0c0h
FIFO_TRIGGER_LEVEL equ FIFO_SIZE_4
FIFO_SETUP equ FIFO_ENABLE+FIFO_CLR_RX+FIFO_CLR_TX+FIFO_TRIGGER_LEVEL
OUTPUT_FIFO_SIZE equ 16
;8250 Line Status Register
LSR_DR equ 1 ;Data ready
LSR_OE equ 2 ;Overrun error
LSR_PE equ 4 ;Parity error
LSR_FE equ 8 ;Framing error
LSR_BI equ 10h ;Break interrupt
LSR_THRE equ 20h ;Transmitter line holding register empty
LSR_TSRE equ 40h ;Transmitter shift register empty
;8250 Interrupt Identification Register
IIR_IP equ 1 ;0 if interrupt pending
IIR_ID equ 6 ;Mask for interrupt ID
IIR_RLS equ 6 ;Receiver Line Status interrupt
IIR_RDA equ 4 ;Receiver data available interrupt
IIR_THRE equ 2 ;Transmitter holding register empty int
IIR_MSTAT equ 0 ;Modem status interrupt
IIR_FIFO_TIMEOUT equ 008h ;FIFO timeout interrupt pending - 16550 only
IIR_FIFO_ENABLED equ 080h ;FIFO enabled (FCR0 = 1) - 16550 only
;8250 interrupt enable register bits
IER_DAV equ 1 ;Data available interrupt
IER_TxE equ 2 ;Tx buffer empty interrupt
IER_RLS equ 4 ;Receive line status interrupt
IER_MS equ 8 ;Modem status interrupt
;8250 Modem control register
MCR_DTR equ 1 ;Data Terminal Ready
MCR_RTS equ 2 ;Request to Send
MCR_OUT1 equ 4 ;Out 1 (not used)
MCR_OUT2 equ 8 ;Master interrupt enable (actually OUT 2)
MCR_LOOP equ 10h ;Loopback test mode
;8250 Modem Status Register
MSR_DCTS equ 1 ;Delta Clear-to-Send
MSR_DDSR equ 2 ;Delta Data Set Ready
MSR_TERI equ 4 ;Trailing edge ring indicator
MSR_DRLSD equ 8 ;Delta Rx Line Signal Detect
MSR_CTS equ 10h ;Clear to send
MSR_DSR equ 20h ;Data set ready
MSR_RI equ 40h ;Ring indicator
MSR_RLSD equ 80h ;Received line signal detect
pr_ch_al macro alvalue
ifdef TRACEON
mov al,alvalue
call pr_ch
endif
endm
;Slip Definitions
FR_END equ 0c0h ;Frame End
FR_ESC equ 0dbh ;Frame Escape
T_FR_END equ 0dch ;Transposed frame end
T_FR_ESC equ 0ddh ;Transposed frame escape
ser_buf_size equ 3000 ; (grg)
BRC equ 115200
MakePublic int_no
TheInfo:
int_no db 4,0,0,0 ; interrupt number.
io_addr dw 03f8h,0 ; I/O address for COM1
baud_rate dw 1234,0 ; support baud higher than 65535
ser_misc db LCR_8BITS,0,0,0 ; misc control bits
hardware_switch db 0,0,0,0 ; if zero, don't use hw handshaking
xmit_time dw 0,0 ; loop timer for ari
baudclk label word
dd BRC ; 1.8432 Mhz / 16
is_16550 db 0 ; 0=no, 1=yes (try using fifo)
MakePublic driver_class, driver_type, driver_name
MakePublic driver_function, parameter_list
driver_class db 6,0,0,0 ;from the packet spec
driver_type db 0,0,0,0 ;from the packet spec
driver_name db 'UMSLIP ',0 ;name of the driver.
driver_function db 2
parameter_list label byte
db 1 ;major rev of packet driver
db 9 ;minor rev of packet driver
db 14 ;length of parameter list
db EADDR_LEN ;length of MAC-layer address
dw GIANT ;MTU, including MAC headers
dw MAX_MULTICAST * EADDR_LEN ;buffer size of multicast addrs
dw 0 ;(# of back-to-back MTU rcvs) - 1
dw 0 ;(# of successive xmits) - 1
int_num dw 0 ;Interrupt # to hook for post-EOI
;processing, 0 == none,
MakePublic recv_buf_size, recv_buf, recv_buf_end, recv_buf_head
MakePublic recv_buf_tail,end_resident
recv_buf_size dw ser_buf_size,0 ;receive buffer size
recv_buf dw ? ;->receive buffer
recv_buf_end dw ? ;->after end of buffer
recv_buf_head dw ? ;->next character to get
recv_buf_tail dw ? ;->next character to store
dummy dw 1234h
IP_TYPE DW 0800H
ifdef debug
MakePublic Pak_Count, xmit_time
endif
Pak_Count dw 0 ; semaphore for packets received
asyrxint_cnt dw 0 ; loop counter in ari
MakePublic rcv_modes
rcv_modes dw 4 ;number of receive modes in our table
dw 0,0,0,rcv_mode_3
MakePublic as_send_pkt
; The Asynchronous Transmit Packet routine.
; Enter with es:di -> i/o control block, ds:si -> packet, cx = packet length,
; interrupts possibly enabled.
; Exit with nc if ok, or else cy if error, dh set to error number.
; es:di and interrupt enable flag preserved on exit.
as_send_pkt:
ret
MakeExternal delaysub:near
MakePublic drop_pkt
; Drop a packet from the queue.
; Enter with es:di -> iocb.
drop_pkt:
assume ds:nothing
ret
MakePublic xmit
; Process a transmit interrupt with the least possible latency to achieve
; back-to-back packet transmissions.
; May only use ax and dx.
xmit:
assume ds:nothing
ret
MakePublic send_pkt
;
; mod 7/25/89 John Grover
; - operates with interrupts on. Xmits one byte per interrupt
; - only turns transmitter buffer empty interrupt off when
; - all bytes of all packets are transmitted.
send_pkt:
;enter with es:di->upcall routine, (0:0) if no upcall is desired.
; (only if the high-performance bit is set in driver_function)
;enter with ds:si -> packet, cx = packet length.
;exit with nc if ok, or else cy if error, dh set to error number.
;called from telnet layer via software interrupt
; We just send each byte in turn. No UART interrupts are needed nor wanted.
; In fact the overdone receiver material omits to note that xmtr interrupts
; can be lost while processing rcvr ones. Small benefits are no stalled
; programs, no transmitter buffer, no problems at 19200 b/s. Joe Doupnik
assume ds:nothing, es:nothing
Show col_InOut,'O'
sti ; enable interrupts
cld
mov al,FR_END ; Flush out any line garbage
call send_char
jc send_pkt_end ; c = failure to send
;Copy input to output, escaping special characters
send_pkt_1:
lodsb
cmp al,FR_ESC ; escape FR_ESC with FR_ESC and T_FR_ESC
jne send_pkt_2
mov al,FR_ESC
call send_char
jc send_pkt_end
mov al,T_FR_ESC
jmp short send_pkt_3
send_pkt_2:
cmp al,FR_END ; escape FR_END with FR_ESC and T_FR_END
jne send_pkt_3
mov al,FR_ESC
call send_char
jc send_pkt_end
mov al,T_FR_END
send_pkt_3:
call send_char
jc send_pkt_end
loop send_pkt_1 ; do cx user characters
mov al,FR_END ; terminate it with a FR_END
call send_char
jc send_pkt_end
clc
send_pkt_end:
Show col_InOut,' '
ret
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
;
; our serial driver
;
MakePublic ser_funcs
ser_DTR:
mov al,MCR_RTS or MCR_OUT2
or cx,cx
jz no_DTR
or al,MCR_DTR
no_DTR: loadport
setport MCR
SlowOut
jmp ser_ret
MakeExternal int_dis:near,DOSPrint:near
ser_setall:
push ds
push es
push si
call int_dis ; return old interrupt
pop si
pop ds ; source segment
push cs
pop es ; dest segment
mov di,offset TheInfo ; dest offset
mov cx,10 ; data length in words
cld
rep movsw
pop ds
call set_recv_isr
call etopen ; reinitialize this new chip
jmp ser_ret
ser_info:
cmp cx,24
jl nov
cld
mov di,si
mov ax,majver
stosw
mov ax,TheVersion
stosw
mov si,offset TheInfo
mov cx,10 ; number of words
rep movsw
nov: jmp ser_ret
ser_lines:
loadport
SetPort MSR
SlowIn
shr ax,4
and ax,000Fh
mov word ptr es:[si],ax
jmp ser_ret
ser_funcs:
cmp al,1
je ser_avail
cmp al,2
je send_raw
cmp al,3
je get_raw
cmp al,4
je ser_DTR
cmp al,5
je ser_setall
cmp al,6
je ser_info
cmp al,7
je ser_lines
stc ; unknown function code
jmp ser_x
ser_ret:
clc ; ok return code
ser_x: ret
ser_avail:
call ser_av
jmp ser_ret
ser_av:
call ser_av_reg
mov es:[si],cx
ret
ser_av_reg:
mov cx,recv_buf_tail
sub cx,recv_buf_head
jge ser_av_1 ; positive length
add cx,recv_buf_size ; negative, add buf size
ser_av_1:
ret
assume ds:code
get_raw: ; read cx chars to es:di
push cs
pop ds ; ds now points to code seg
mov dx,cx
call ser_av_reg ; find amount available
cmp dx,cx
jle get_lok
mov dx,cx ; trim request to avail
get_lok:
mov cx,dx ; requested length
mov ax,cx
cld
stosw ; return cx to caller
or cx,cx
jle get_ex ; requested <= 0 !!
mov si,recv_buf_head
get_loop:
lodsb ; get byte from buffer
stosb ; store in user's buffer
cmp si,recv_buf_end
jb get_bot
mov si,recv_buf ; back to top of buffer
get_bot:
loop get_loop
mov recv_buf_head,si ; update head ptr
get_ex:
mov cx,dx ; return length read
jmp ser_ret
assume ds:nothing, es:nothing
;
; send_raw: send a stream of characters (no interpretation)
;
;
send_raw: ; send cx chars from ds:si
sti ; enable interrupts
cld
push es
pop ds
or cx,cx
jle send_raw_end
send_raw_1:
lodsb
call send_char
jc send_raw_end
loop send_raw_1 ; do cx user characters
clc
send_raw_end:
jmp ser_ret ; done ok
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
;----------------------------------------------------------------------------
; mod 7/25/89 John Grover
; redone by Joe Doupnik, Dec 1991
;
;
assume ds:nothing, es:nothing
send_char: ; send the character in al
ifdef grg
push es
push ax
mov ax,0B800h
mov es,ax
inc byte ptr es:[col_chout]
pop ax
pop es
endif
push dx
push cx
xchg ah,al ; put data char into ah
xor cx,cx ; 64K retry counter
mov bl,hardware_switch ; Get hardware check (CTS) switch
outc1: loadport
SetPort LSR
SlowIn
test al,LSR_THRE ; Transmitter (THRE) ready?
jnz outc2 ; nz = yes
loop outc1
stc ; carry set for failure
jmp short outc3 ; timeout
outc2: xor cx,cx ; reset retry counter
or bl,bl ; Yes -- check CTS ready if needed.
jz outc25 ; No check -- send the character.
SetPort MSR
SlowIn
test al,MSR_CTS ; Is CTS ready?
jz outc1 ; no, go check again
outc25: xchg al,ah ; now send it
mov dx,io_addr
Delay
SlowOut ; send the byte
clc ; status of success
outc3: pop cx
pop dx
ret
MakePublic get_address
get_address:
;get the address of the interface.
;enter with es:di -> place to get the address, cx = size of address buffer.
;exit with nc, cx = actual size of address, or cy if buffer not big enough.
assume ds:code
xor cx,cx
clc
ret
MakePublic set_address
set_address:
;set the address of the interface.
;enter with es:di -> place to get the address, cx = size of address buffer.
;exit with nc, cx = actual size of address, or cy if buffer not big enough.
assume ds:nothing
clc
ret
rcv_mode_3:
;receive mode 3 is the only one we support, so we don't have to do anything.
ret
MakePublic set_multicast_list
set_multicast_list:
;enter with ds:si ->list of multicast addresses, cx = number of addresses.
;return nc if we set all of them, or cy,dh=error if we didn't.
mov dh,NO_MULTICAST
stc
ret
MakePublic get_multicast_list
get_multicast_list:
;return with nc, es:di ->list of multicast addresses, cx = number of bytes.
;return cy, NO_ERROR if we don't remember all of the addresses ourselves.
;return cy, NO_MULTICAST if we don't implement multicast.
mov dh,NO_MULTICAST
stc
ret
MakePublic terminate
terminate:
ret
MakePublic reset_interface
reset_interface:
;reset the interface.
assume ds:code
ret
;called when we want to determine what to do with a received packet.
;enter with cx = packet length, es:di -> packet type, dl = packet class.
MakeExternal recv_find: near
;called after we have copied the packet into the buffer.
;enter with ds:si ->the packet, cx = length of the packet.
MakeExternal recv_copy: near
MakeExternal count_in_err: near
MakeExternal count_out_err: near
MakePublic recv
;
; mod 7/25/89 John Grover
;
;called from the recv isr. All registers have been saved, and ds=cs.
;Upon exit, the interrupt will be acknowledged.
assume ds:code
recv:
rci_mo: loadport
setport IIR
SlowIn ; any interrupts at all?
test al,IIR_IP
jne rci_x ; no.
and al,IIR_ID
cmp al,IIR_RDA ; Receiver interrupt
je ari
rci_ni: cmp al,IIR_MSTAT
jne rci_x
setport MSR ; make sure of CTS status
SlowIn
;;;; test al, MSR_CTS ; test removed?????
jmp rci_mo
rci_x: ret
;-------------------------------------------------------------------------
;Process 8250 receiver interrupts
;
; mod 7/25/89 John Grover
; - this branches off when bps < 9600. See ari_a.
; - Above 9600 bps we go into a loop to process a packet at
; - a time. If not data ready for a certain amount of time,
; - the process exits and waits for the next byte. This certain
; - amount of time to wait depends on the bps and CPU processor speed
; - and is determined in the initialization of the driver.
; - Upon receiving the FR_END character for the first frame in the
; - buffer a semaphore is set which tells rf to run.
ari: mov bx,ds ; get set up for the routine
mov es,bx
mov bx,xmit_time ; is zero if no polling desired
ari_a: mov di,recv_buf_tail
xor bp,bp ; set flag to indicate 1st char processed
mov si,Pak_Count ; optimization
mov ah,LSR_DR
ari_again:
xor cx,cx ; initialize counter
loadport
setport LSR
ari_in: SlowIn
test al,LSR_OE
jz noovr
ShowInc col_ovrr,'O'
noovr: test al,LSR_FE
jz nofr
ShowInc col_frame,'F'
nofr: test al,ah
jnz ari_gd ; yes - break out of loop
inc cx ; no - increase loop counter
cmp cx,bx ; timeout?
jae ari_ex ; yes - leave
jmp ari_in ; no - keep looping
;------ got data ready, read the char
ari_gd: setport RBR
SlowIn
; Process incoming data;
; If buffer is full, we have no choice but
; to drop the character
cmp di,recv_buf_head
jne ari_ok ; none - continue
ShowInc col_serfull,'S' ; twiddle screen if buf full
or si,si ; maybe - if there are packets
jnz ari_ex ; yes exit
ari_ok: stosb
cmp di,recv_buf_end ; did we hit the end of the buffer?
jne ari_3 ; no.
mov di,recv_buf ; yes - wrap around.
ari_3: cmp al,FR_END ; might this be the end of a frame?
jne ari_reset ; no - reset flag and loop
inc si ; yes - indicate packet ready
or bp,bp ; was this the first char?
jne ari_ex ; no - exit handler
ari_reset:
inc bp ; set 1st character flag
jmp ari_again ; get another character
ari_ex: mov recv_buf_tail,di
mov Pak_Count, si
mov bx,bp
add bl,'0'
Show col_serfull,bl ; show char gotten count
jmp rci_x
; --------------------------------------------------------------
;
; recv_exiting
;
MakePublic recv_exiting
recv_exiting:
cmp Pak_Count,0 ; is a packet ready?
je recv_isr_exit ; no - skip to end
push ax
push bx
push cx
push dx
push ds
push es
push bp
push di
push si
push cs ; point ds properly
pop ds
sti ; enable interrupts
call rf
cli
pop si
pop di
pop bp
pop es
pop ds
pop dx
pop cx
pop bx
pop ax
recv_isr_exit:
ret
; --------------------------------------------------------------
;
; rf
;
; mod 7/25/89 John Grover
;
; - rf now operates with interrupts on. It is triggered
; - by the Pak_Count flag and continues until all bytes
; - in all packets in the buffer have been transmitted to the upper
; - layer.
;
ifdef debug
MakePublic rf
endif
rf: cmp Pak_Count, 0 ; should we do this?
jle rf_end ; no - exit
rf_0: mov si,recv_buf_head ;process characters.
xor cx,cx ;count up the size here.
cld ; quite important
rf_1: call recv_char ;get a char.
je rf_2 ;go if no more chars.
cmp al,FR_ESC ;an escape?
je rf_1 ;yes - don't count this char.
inc cx ;no - count this one.
jmp rf_1
rf_2: jcxz rf_3z ;count zero? yes - free the frame
;we don't need to set the type because none are defined for SLIP.
push si ;save si in case we reject it.
push bx
MOV DI,CS
MOV ES,DI
mov di,0
mov dl,cs:driver_class
call recv_find ;look up our type.
pop bx
pop si
mov ax,es ;is this pointer null?
or ax,di
je rf_3 ;yes - just free the frame.
push cx
push es ;remember where the buffer pointer is
push di
mov si,recv_buf_head ;process characters.
rf_4: call recv_char
je rf_6 ;yes - we're all done.
cmp al,FR_ESC ;an escape?
jne rf_5 ;no - just store it.
call recv_char ;get the next character.
je rf_6
cmp al,T_FR_ESC
mov al,FR_ESC ;assume T_FR_ESC
je rf_5 ;yup, that's it - store FR_ESC
mov al,FR_END ;nope, store FR_END
rf_5: stosb ;store the byte.
jmp rf_4
rf_6: mov recv_buf_head,si ;we're skipped to the end.
pop si ;now give the frame to the client.
pop ds
pop cx
assume ds:nothing
call recv_copy ; do the upcall to the tcp driver
push cs
pop ds
assume ds:code
jmp rf_end
rf_3:
ShowInc col_TCPFull,'T'
rf_3z: mov recv_buf_head,si ;remember the new starting point.
rf_end: dec Pak_Count
cmp Pak_Count,0 ; are there more packets ready?
jbe rf_9
jmp rf_0 ; yes - execute again
rf_9: ret
; --------------------------------------------------------------
;
; recv_char
;
; mod 7/25/89 John Grover
; - Now uses buffer pointers to determine if there are
; - characters left.
;
recv_char:
;enter with si -> receive buffer, bx = receive count. Wrap around if needed.
;return with nz, al = next char. Return zr if there are no more chars in
; this frame.
;
lodsb
cmp si,recv_buf_end
jb recv_char_1
mov si,recv_buf
recv_char_1:
mov bx, recv_buf_tail
cmp si, bx
je recv_char_2
cmp al,FR_END
recv_char_2:
ret
set_baud:
;compute the divisor given the baud rate.
mov dx,baudclk+2
mov ax,baudclk
mov bx,0
sb_1: inc bx
sub ax,baud_rate
sbb dx,baud_rate+2
jnc sb_1
dec bx
add ax,baud_rate
adc dx,baud_rate+2
sb_2: call flush_chars
loadport ;Purge the receive data buffer
mov ah,LCR_DLAB ;Turn on divisor latch access bit
setport LCR
call setbit
mov al,bl ;Load the two bytes of the divisor.
setport DLL
SlowOut
mov al,bh
setport DLM
SlowOut
mov ah,LCR_DLAB ;Turn off divisor latch access bit
setport LCR
call clrbit
ret
flush_chars:
loadport ;Purge the receive data buffer
mov cx,1000 ;flush up to 1K chars
flush_next:
SlowIn
test al,LSR_DR
jz flush_done ; yes - break out of loop
setport RBR
SlowIn ; read and ignore the char
loop flush_next
flush_done:
ret
; --------------------------------------------------------------
;
; etopen
;
; mod 7/25/89 John Grover
; - Contains a loop to determine a pseudo timeout for ari.
; - The value is determined by transmitting characters in a
; - loop whose clock cycles are nearly the same as the "sister"
; - loop in ari. The per character, maximum time used
; - basis which is then multiplied by a factor to achieve a timeout
; - value for the particular bps and CPU speed of the host.
MakePublic etopen
etopen:
pushf
cli
;
; mod 3/16/90 Denis DeLaRoca
; - determine if 16550 uart is present
; - if so initialize fifo buffering
;
loadport
setport FCR
mov al,FIFO_ENABLE
SlowOut ;outportb(base+FCR,(char) FIFO_ENABLE)
setport IIR
SlowIn ;inportb(base+IIR)
and al,IIR_FIFO_ENABLED ; & IIR_FIFO_ENABLED
cmp al,IIR_FIFO_ENABLED ;both bits must be on NEW, 11/20/90
jnz not_16550 ;nope, we don't have 16550 chip
mov is_16550,1 ;yes, note fact
mov al,FIFO_SETUP ;and setup FIFO
setport FCR
SlowOut ;outportb(base+FCR,(char) FIFO_SETUP)
not_16550:
call flush_chars
;Set line control register: 8 bits, no parity
loadport
mov al,ser_misc
setport LCR
SlowOut
;Turn on receive interrupt enable in 8250, leave transmit
; and modem status interrupts turned off for now
mov al,IER_DAV
setport IER
SlowOut
; Set modem control register: assert DTR, RTS, turn on 8250
; master interrupt enable (connected to OUT2)
mov al,MCR_DTR or MCR_RTS or MCR_OUT2
setport MCR
SlowOut
call set_baud
call set_recv_isr ;Set interrupt vector to SIO handler
;set up the various pointers.
mov dx,offset end_resident
mov recv_buf,dx
mov recv_buf_head,dx
mov recv_buf_tail,dx
add dx,recv_buf_size
mov recv_buf_end,dx
xor si,si
cmp baud_rate,9600 ; below 19200 we're strictly
jbe t_a ; interrupt driven
; the following code attempts to determine a pseudo timeout
; value to use in the loop that waits for an incoming character
; in ari. The value returned in xmit_time is the number of
; loops processed between characters - therefore the loop used below
; is and should remain similar to the loop used in ari.
mov ah,LSR_THRE
mov cx,16 ; take the highest of 16 runs
xor si,si ; will hold highest value
xmit_time_start:
xor di,di ; initialize counter
loadport
setport THR ; xmit a character
mov al,' ' ; send a space to minimize damage
SlowOut
setport LSR ; set up to check for an empty buffer
; next is the loop actually being timed
mov bx,1 ; wait up to 65K times
xmit_time_top:
SlowIn
test al,ah
jnz xmit_time_done
inc di
cmp cx,cx ; these next few instructions do nothing
jmp xmit_time_1 ; except maintain similarity with the
; "sister" loop in ari
xmit_time_1:
inc bx
jmp xmit_time_top
xmit_time_done: ; end of timed loop
cmp si,di ; compare highest value with new value
ja xmit_time_end ; no bigger - just loop
mov si,di ; bigger - save it
xmit_time_end:
loop xmit_time_start ; bottom of outer loop
call flush_chars ; throw away any echoed zeroes
shl si,3 ; we'll wait 8 characters worth
t_a: mov xmit_time,si ; retain largest value
mov al, int_no ; Get board's interrupt vector
add al, 8
cmp al, 8+8 ; Is it a slave 8259 interrupt?
jb set_int_num ; No.
add al, 70h - 8 - 8 ; Map it to the real interrupt.
set_int_num:
xor ah, ah ; Clear high byte
mov int_num, ax ; Set parameter_list int num.
popf
clc ;indicate no errors.
ret
;Set bit(s) in I/O port
setbit:
;enter with dx = port, ah = bit to set.
SlowIn
or al,ah
SlowOut
ret
;Clear bit(s) in I/O port
clrbit:
;enter with dx = port, ah = bit to set.
SlowIn
not al ;perform an and-not using DeMorgan's.
or al,ah
not al
SlowOut
ret
is_nochip_msg: db 'Serial Port is not there??',CR,LF,'$'
;**************************************************************************
;**************************************************************************
;**************************************************************************
;any code after this will not be kept after initialization.
end_resident label byte
;**************************************************************************
;**************************************************************************
;**************************************************************************
MakePublic usage_msg
usage_msg db "usage: UMSLIP [?] [-n] [-w] [-s] [packet_int_no]"
db " [recv_buf_size]",CR,LF,'$'
MakePublic copyright_msg
copyright_msg db "Packet driver for SLIP, version ",'0'+majver mod 10
db ".",'0'+TheVersion mod 10,CR,LF
db "Portions Copyright 1988 Phil Karn,"
db " 1991 Joe Doupnik,",CR,LF
db "Portions Copyright 1993 Univ. of Minnesota",cr,lf,'$'
MakeExternal set_recv_isr: near
;enter with si -> argument string, di -> word to store.
;if there is no number, don't change the number.
MakeExternal get_number: near
;enter with dx -> name of word, di -> dword to print.
MakeExternal print_number: near
MakeExternal print_number_dec: near
MakeExternal print_number_hex: near
MakeExternal print_crlf: near
;enter with si -> argument string.
;skip spaces and tabs. Exit with si -> first non-blank char.
MakeExternal skip_blanks: near
MakePublic parse_args
parse_args:
mov di,offset recv_buf_size
call get_number
clc
ret
code ends
; PC/FTP Packet Driver source, conforming to version 1.05 of the spec
; Updated to version 1.08 Feb. 17, 1989.
; Copyright 1988-1992 Russell Nelson
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General MakePublic License as published by
; the Free Software Foundation, version 1.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General MakePublic License for more details.
;
; You should have received a copy of the GNU General MakePublic License
; along with this program; if not, write to the Free Software
; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
code segment word
assume cs:code, ds:code
MakeExternal phd_dioa: byte
MakeExternal phd_environ: word
MakeExternal flagbyte: byte,quiet:byte,end_resident:near,recv_buf_size:word
;put into the MakePublic domain by Russell Nelson, nelson@crynwr.com
MakePublic print_number_hex
print_number_hex:
Print
mov al,'0'
call chrout
mov al,'x'
call chrout
mov ax,[di] ;print the number in hex.
mov dx,[di+2]
call dwordout
ret
MakePublic print_number_dec
print_number_dec:
or dx,dx
je NoTxt
Print
NoTxt: mov ax,[di] ;print the number in decimal.
mov dx,[di+2]
call decout
ret
MakePublic print_crlf
print_crlf:
mov al,CR
call chrout
mov al,LF
call chrout
ret
MakePublic print_number
print_number:
;enter with dx -> dollar terminated name of number, di ->dword.
;exit with the number printed and the cursor advanced to the next line.
call print_number_hex
mov al,' '
call chrout
mov al,'('
call chrout
mov dx,0
call print_number_dec
mov al,')'
call chrout
call print_crlf
ret
;put into the MakePublic domain by Russell Nelson, nelson@crynwr.com
MakePublic decout
decout:
mov si,ax ;get the number where we want it.
mov di,dx
or ax,dx ;is the number zero?
jne decout_nonzero
mov al,'0' ;yes - easier to just print it, than
jmp chrout ; to eliminate all but the last zero.
decout_nonzero:
xor ax,ax ;start with all zeroes in al,bx,bp
mov bx,ax
mov bp,ax
mov cx,32 ;32 bits in two 16 bit registers.
decout_1:
shl si,1
rcl di,1
xchg bp,ax
call addbit
xchg bp,ax
xchg bx,ax
call addbit
xchg bx,ax
adc al,al
daa
loop decout_1
mov cl,'0' ;prepare to eliminate leading zeroes.
call byteout ;output the first two.
mov ax,bx ;output the next four
call wordout ;output the next four
mov ax,bp
jmp wordout
addbit: adc al,al
daa
xchg al,ah
adc al,al
daa
xchg al,ah
ret
;put into the MakePublic domain by Russell Nelson, nelson@crynwr.com
MakePublic dwordout, wordout, byteout, digout
dwordout:
mov cl,'0' ;prepare to eliminate leading zeroes.
xchg ax,dx ;just output 32 bits in hex.
call wordout ;output dx.
xchg ax,dx
wordout:
push ax
mov al,ah
call byteout
pop ax
byteout:
mov ah,al
shr al,1
shr al,1
shr al,1
shr al,1
call digout
mov al,ah
digout:
and al,0fh
add al,90h ;binary digit to ascii hex digit.
daa
adc al,40h
daa
cmp al,cl ;leading zero?
je digout_1
mov cl,-1 ;no more leading zeros.
jmp chrout
digout_1:
ret
;put into the MakePublic domain by Russell Nelson, nelson@crynwr.com
MakePublic chrout
chrout:
push ax ;print the char in al.
push dx
mov dl,al
test quiet,1
jnz nochout
mov ah,2
int 21h
nochout:
pop dx
pop ax
ret
;*************************************************************************
;usage_msg is of the form "usage: driver [-d -n] <packet_int_no> <args>"
MakeExternal usage_msg: byte
;copyright_msg is of the form:
;"Packet driver for the foobar",CR,LF
;"Portions Copyright 19xx, J. Random Hacker".
MakeExternal copyright_msg: byte
copyleft_msg label byte
db "Packet driver skeleton copyright 1988-92, Crynwr Software.",CR,LF
db "This program is free software; see the file COPYING for details.",CR,LF
db "NO WARRANTY; see the file COPYING for details."
crlf_msg db CR,LF,'$'
no_resident_msg label byte
db CR,LF,"*** Packet driver failed to initialize the board ***",CR,LF,'$'
;parse_args should parse the arguments.
;called with ds:si -> immediately after the packet_int_no.
MakeExternal parse_args: near
;print_parameters should print the arguments.
MakeExternal print_parameters: near
MakeExternal our_isr: near, their_isr: dword
MakeExternal ser_their_isr: dword
MakeExternal packet_int_no: byte
MakeExternal is_at: byte, sys_features: byte
MakeExternal int_no: byte
MakeExternal driver_class: byte
packet_int_no_name db "Packet interrupt number ",'$'
eaddr_msg db "My Ethernet address is ",'$'
aaddr_msg db "My ARCnet address is ",'$'
already_msg db CR,LF,"There is already a packet driver at ",'$'
int_msg db CR,LF
db "Error: <int_no> should be between 0 and "
int_msg_num label word
db "15 inclusive", '$'
our_address db EADDR_LEN dup(?)
MakePublic etopen_diagn
etopen_diagn db 0 ; errorlevel from etopen if set
;etopen should initialize the device. If it needs to give an error, it
;can issue the error message and quit to dos.
MakeExternal etopen: near
;get the address of the interface.
;enter with es:di -> place to get the address, cx = size of address buffer.
;exit with nc, cx = actual size of address, or cy if buffer not big enough.
MakeExternal get_address: near
already_error:
mov quiet,0
mov dx,offset already_msg
mov di,offset packet_int_no
call print_number
mov ax,4c05h ; give errorlevel 5
int 21h
usage_error:
mov dx,offset usage_msg
MakePublic error
error:
mov quiet,0
Print
mov ax,4c0ah ; give errorlevel 10
int 21h
MakePublic start_1
start_1:
cld
;
; Get the feature byte (if reliable) so we can know if it is a microchannel
; computer and how many interrupts there are.
;
mov ah,0c0h
int 15h ; es:bx <- sys features block
jc look_in_ROM ; error, must use rom.
or ah,ah
jnz look_in_ROM
mov dx,es:[bx] ; # of feature bytes
cmp dx,4 ; do we have the feature byte we want?
jae got_features ;yes.
look_in_ROM:
mov dx,0f000h ;ROM segment
mov es,dx
cmp byte ptr es:[0fffeh],0fch;is this an AT?
jne identified ;no.
or sys_features,TWO_8259 ; ATs have 2nd 8259
jmp identified ; assume no microchannel
got_features:
mov ah,es:[bx+2] ; model byte
cmp ah,0fch
je at_ps2
ja identified ; FD, FE and FF are not ATs
cmp ah,0f8h
je at_ps2
ja identified ; F9, FA and FB are not ATs
cmp ah,09ah
jbe identified ; old non-AT Compacs go here
at_ps2: ; 9B - F8 and FC are assumed to
mov ah,es:[bx+5] ; have reliable feature byte
mov sys_features,ah
identified:
mov si,offset phd_dioa+1
call skip_blanks ;end of line?
cmp al,CR
je nopknum ; yes, assume 0x60
chk_options:
call skip_blanks
cmp al,'-' ; any options?
jne no_more_opt
inc si ; skip past option char
lodsb ; read next char
or al,20h ; convert to lower case
cmp al,'n'
jne not_n_opt
or flagbyte,N_OPTION
jmp chk_options
not_n_opt:
cmp al,'w'
jne not_w_opt
or flagbyte,W_OPTION
jmp chk_options
not_w_opt:
cmp al,'s'
jne usage_error_j_1
mov quiet,1
jmp chk_options
usage_error_j_1:
jmp usage_error
no_more_opt:
cmp al,'?'
je usage_error_j_1
mov di,offset packet_int_no ;parse the packet interrupt number
mov bx,offset packet_int_no_name
call get_number ; for them.
call parse_args
jc usage_error_j_1
call skip_blanks ;end of line?
cmp al,CR
jne usage_error_j_1
nopknum:
call verify_packet_int
jnc packet_int_ok
jmp error
packet_int_ok:
jne packet_int_unused
jmp already_error ;give an error if there's one there.
packet_int_unused:
;
; Verify that the interrupt number they gave is valid.
;
cmp int_no,15 ;can't possibly be > 15.
ja int_bad
test sys_features,TWO_8259 ; 2nd 8259 ?
jnz int_ok ;yes, no need to check for <= 7.
mov int_msg_num,'7'+' '*256 ;correct the error message, just in case.
cmp int_no,7 ;make sure that the packet interrupt
jbe int_ok ; number is in range.
int_bad:
mov dx,offset int_msg
jmp error
int_ok:
;
; Map IRQ 2 to IRQ 9 if needed.
;
test sys_features,TWO_8259 ; 2nd 8259 ?
je no_mapping_needed ;no, no mapping needed
cmp int_no,2 ;map IRQ 2 to IRQ 9.
jne no_mapping_needed
mov int_no,9
no_mapping_needed:
mov dx,offset copyright_msg
Print
mov dx,offset copyleft_msg
Print
call take_packet_int
mov ah,49h ;free our environment, because
mov es,phd_environ ; we won't need it.
int 21h
mov bx,1 ;get the stdout handle.
mov ah,3eh ;close it in case they redirected it.
int 21h
mov dx,offset end_resident
add dx,recv_buf_size
add dx,0fh ;round up to next highest paragraph.
mov cl,4
shr dx,cl
mov ah,31h ;terminate, stay resident.
mov al,etopen_diagn ; errorlevel (0 - 9, just diagnostics)
int 21h
no_resident:
mov dx,offset no_resident_msg
Print
mov ax,4c00h + 32 ; give errorlevel 32
cmp al,etopen_diagn
ja no_et_diagn ; etopen gave specific reason?
mov al,etopen_diagn ; yes, use that for error level
no_et_diagn:
int 21h
; Suggested errorlevels:
;
; _____________________ 0 = normal
; 1 = unsuitable memory address given; corrected
; In most cases every- 2 = unsuitable IRQ level given; corrected
; thing should work as 3 = unsuitable DMA channel given; corrected
; expected for lev 1-5 4 = unsuitable IO addr given; corrected (only 1 card)
; _____________________ 5 = packet driver for this int # already loaded
; External errors, when 20 = general cable failure (but pkt driver is loaded)
; corrected normal 21 = network cable is open -"-
; operation starts 22 = network cable is shorted -"-
; _____________________ 23 =
; Packet driver not 30 = usage message
; loaded. A new load 31 = arguments out of range
; attempt must be done 32 = unspecified device initialization error
; 33 =
; 34 = suggested memory already occupied
; 35 = suggested IRQ already occupied
; 36 = suggested DMA channel already occupied
; 37 = could not find the network card at this IO address
take_packet_int:
mov ah,35h ;remember their packet interrupt.
mov al,packet_int_no
int 21h
mov their_isr.offs,bx
mov their_isr.segm,es
mov ah,25h ;install our packet interrupt
mov dx,offset our_isr
int 21h
ret
MakePublic DOSPrint
DOSPrint:
test quiet,1
jnz NoPrint
mov ah,9
int 21h
NoPrint:
ret
signature db 'PKT DRVR',0
signature_len equ $-signature
packet_int_msg db CR,LF
db "Error: <packet_int_no> should be in the range 0x60 to 0x80"
db '$'
verify_packet_int:
;enter with no special registers.
;exit with cy,dx-> error message if the packet int was bad,
; or nc,zr,es:bx -> current interrupt if there is a packet driver there.
; or nc,nz,es:bx -> current interrupt if there is no packet driver there.
cmp packet_int_no,60h ;make sure that the packet interrupt
jb verify_packet_int_bad ; number is in range.
cmp packet_int_no,80h
jbe verify_packet_int_ok
verify_packet_int_bad:
mov dx,offset packet_int_msg
stc
ret
verify_packet_int_ok:
mov ah,35h ;get their packet interrupt.
mov al,packet_int_no
int 21h
lea di,3[bx] ;see if there is already a signature
mov si,offset signature ; there.
mov cx,signature_len
repe cmpsb
clc
ret
;put into the MakePublic domain by Russell Nelson, nelson@crynwr.com
MakePublic get_number
get_number:
mov bp,10 ;we default to 10.
jmp short get_number_0
MakePublic get_hex
get_hex:
mov bp,16
;get a hex number, skipping leading blanks.
;enter with si->string of digits,
; di -> dword to store the number in. [di] is not modified if no
; digits are given, so it acts as the default.
;return cy if there are no digits at all.
;return nc, bx:cx = number, and store bx:cx at [di].
get_number_0:
call skip_blanks
call get_digit ;is there really a number here?
jc get_number_3
xor ah,ah
cmp ax,bp ;larger than our base?
jae get_number_3 ;yes.
or al,al ;Does the number begin with zero?
jne get_number_4 ;no.
mov bp,8 ;yes - they want octal.
get_number_4:
xor cx,cx ;get a hex number.
xor bx,bx
get_number_1:
lodsb
cmp al,'x' ;did they really want hex?
je get_number_5 ;yes.
cmp al,'X' ;did they really want hex?
je get_number_5 ;yes.
call get_digit ;convert a character into an int.
jc get_number_2 ;not a digit (neither hex nor dec).
xor ah,ah
cmp ax,bp ;larger than our base?
jae get_number_2 ;yes.
push ax ;save the new digit.
mov ax,bp ;multiply the low word by ten.
mul cx
mov cx,ax ;keep the low word.
push dx ;save the high word for later.
mov ax,bp
mul bx
mov bx,ax ;we keep only the low word (which is our high word)
pop dx
add bx,dx ;add the high result from earlier.
pop ax ;get the new digit back.
add cx,ax ;add the new digit in.
adc bx,0
jmp get_number_1
get_number_5:
mov bp,16 ;change the base to hex.
jmp get_number_1
get_number_2:
dec si
mov [di],cx ;store the parsed number.
mov [di+2],bx
clc
jmp short get_number_6
get_number_3:
cmp al,'?' ;did they ask for the default?
stc
jne get_number_6 ;no, return cy.
add si,1 ;skip past the question mark.
clc
get_number_6:
ret
;put into the MakePublic domain by Russell Nelson, nelson@crynwr.com
MakePublic get_digit
get_digit:
;enter with al = character
;return nc, al=digit, or cy if not a digit.
cmp al,'0' ;decimal digit?
jb get_digit_1 ;no.
cmp al,'9' ;. .?
ja get_digit_2 ;no.
sub al,'0'
clc
ret
get_digit_2:
cmp al,'a' ;hex digit?
jb get_digit_3
cmp al,'f' ;hex digit?
ja get_digit_3
sub al,'a'-10
clc
ret
get_digit_3:
cmp al,'A' ;hex digit?
jb get_digit_1
cmp al,'F' ;hex digit?
ja get_digit_1
sub al,'A'-10
clc
ret
get_digit_1:
stc
ret
;put into the MakePublic domain by Russell Nelson, nelson@crynwr.com
MakePublic skip_blanks
skip_blanks:
lodsb ;skip blanks.
cmp al,' '
je skip_blanks
cmp al,HT
je skip_blanks
dec si
ret
MakePublic print_ether_addr
print_ether_addr:
mov cx,EADDR_LEN
print_ether_addr_0:
push cx
lodsb
mov cl,' ' ;Don't eliminate leading zeroes.
call byteout
pop cx
cmp cx,1
je print_ether_addr_1
mov al,':'
call chrout
print_ether_addr_1:
loop print_ether_addr_0
ret
code ends
end start